package com.cymal;


import com.cymal.condition.Condition;
import com.cymal.entity.bo.Result;
import com.cymal.entity.po.subject.AbstractSubject;
import com.cymal.search.Searcher;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 判题机引擎, 实时去缓存进行判题, 减少机器成本
 */
@Component
@Slf4j
public class SubjectEngine<Subject extends AbstractSubject, Req> {


    /**
     * 条件匹配器
     */
    private final List<Condition> conditions;


    /**
     * 解析搜寻器
     */
    private final List<Searcher> searchers;


    /**
     * Constructor.
     * @param conditions 条件匹配器
     * @param searchers 解析搜寻器
     */
    public SubjectEngine(List<Condition> conditions, List<Searcher> searchers) {
        this.conditions = conditions;
        this.searchers = searchers;
    }


    /**
     * 子引擎数
     */
    private static final int CHILD_ENGINE_NUM = 2;


    /**
     * 匹配池
     */
    private final ExecutorService ces = new EngineThreadPoolExecutor("conditioner-work-",300, 100);


    /**
     * 搜寻池
     */
    private final ExecutorService ses = new EngineThreadPoolExecutor("searcher-work-",300, 100);


    /**
     * 判题匹配
     *
     * @param subject 题目
     * @param reqs    请求
     * @return result.
     */
    public Result matched(Subject subject, Collection<Req> reqs) {

        Result finalResult = new Result();
        final CountDownLatch count = new CountDownLatch(CHILD_ENGINE_NUM);
        try {
            ces.submit(() -> {
                Optional<Condition> conditionResult = conditions.stream()
                        .filter(condition -> Objects.equals(condition.conditionType(), subject.getSubjectType()))
                        .findFirst();
                Condition handleCondition = conditionResult.orElseThrow();
                finalResult.setFinalResult(handleCondition.matched(subject, reqs));
                count.countDown();
            });
        } catch (Exception e) {
            log.error("判题机匹配失败", e);
        }
        try {
            ses.submit(() -> {
                Optional<Searcher> searchResule = searchers.stream()
                        .filter(searcher -> Objects.equals(searcher.searchType(), subject.getSubjectType()))
                        .findFirst();
                Searcher handleSearcher = searchResule.orElseThrow();
                finalResult.setAnswers(handleSearcher.search(subject));
                count.countDown();
            });
        } catch (Exception e) {
            log.error("判题机搜寻失败", e);
        }
        try {
            count.await();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return finalResult;

    }


    /**
     * 引擎异步匹配搜寻线程池
     */
    class EngineThreadPoolExecutor extends ThreadPoolExecutor {


        /**
         * Constructor.
         * @param threadName 线程名
         * @param keepAliveTime 淘汰存活时间
         * @param queueCapacity 阻塞队列长度
         */
        public EngineThreadPoolExecutor(String threadName ,long keepAliveTime, int queueCapacity) {
            super(Runtime.getRuntime().availableProcessors(),
                    Runtime.getRuntime().availableProcessors(),
                    keepAliveTime,
                    TimeUnit.SECONDS,
                    new ArrayBlockingQueue<>(queueCapacity),
                    new ThreadFactory() {
                        AtomicInteger threadCount = new AtomicInteger(0);
                        @Override
                        public Thread newThread(Runnable runnable) {
                            final Thread thread = new Thread(runnable, threadName + threadCount.getAndIncrement());
                            thread.setDaemon(true);
                            return thread;
                        }
                    },
                    new ThreadPoolExecutor.AbortPolicy()
            );
        }

    }


}
